/* HttpClient Utils of smartwork frame work by lms 2016.06.21
 * first, you need to new HttpTool
 * then you can getHttpClient to obtain the httpClient
 * you need to dispose HttpTool when you finish your work
 * last update by lms 2017.05.04 to support https request
 * */

package com.fzrskj.cloud.free.core.tool;

import ms.core.tool.StrTool;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

public class CHttpTool {
	private int rTimeout = 0;
	private int cTimeout = 0;
	private CloseableHttpClient httpClient = null;
	List<NameValuePair> requestHeader=null;
	private static Logger logger = LogManager.getLogger(ms.core.tool.HttpTool.class);
	//constructor function to initialize HttpClient
	public CHttpTool(){
		httpClient = HttpClients.createDefault();
	}
	
	public CHttpTool(boolean redirect){
		if (!redirect) httpClient = HttpClients.custom().disableRedirectHandling().build();
		else httpClient = HttpClients.custom().setRedirectStrategy(new LaxRedirectStrategy()).build();
	}

	public CHttpTool(boolean https, boolean redirect){
		if (!https) {
			if (redirect) httpClient = HttpClients.custom().setRedirectStrategy(new LaxRedirectStrategy()).build();
			else httpClient = HttpClients.custom().disableRedirectHandling().build();
		}
		else {
			if (redirect)
				httpClient = HttpClients.custom().setRedirectStrategy(new LaxRedirectStrategy()).setSSLSocketFactory(createSSLConnSocketFactory()).build();
			else
				httpClient = HttpClients.custom().disableRedirectHandling().setSSLSocketFactory(createSSLConnSocketFactory()).build();				
		}
	}
	
	public void setTimeout(int connTimeout, int readTimeout){
		this.cTimeout = connTimeout;
		this.rTimeout = readTimeout;
	}

	//to close CloseableHttpClient
	public void dispose(){
		closeHttpClient();
	}

	//to get HttpClient Object
	public CloseableHttpClient getHttpClient(){
		return this.httpClient;
	}

	/**set cookie information to http request
	 * by lms 2016.07.04
	 * */
	public void setCookie(Map<String,String> cks){
		CookieStore cookie = new BasicCookieStore();
		for(Map.Entry<String, String> item: cks.entrySet()){
			BasicClientCookie ck = new BasicClientCookie(item.getKey(),item.getValue());
			cookie.addCookie(ck);
		}
		HttpClients.custom().setDefaultCookieStore(cookie).build();
	}

	/**set http request header
	 * @param header key=value,such as: Accept-Encoding=UTF-8
	 */
	public void setRequestHeader(List<NameValuePair> header){
		this.requestHeader = header;
	}
	
	/**to update request header
	 * @param header
	 */
	private void updateRequestHeader(AbstractHttpMessage header){
		if (this.requestHeader==null) return;

		for (NameValuePair item: requestHeader){
			header.addHeader(item.getName(), item.getValue());
		}
	}

	//execute url to do http request and return content
	public String httpGet(String url){
		String ret = "";
		if (StrTool.isBlankStr(url))
			return null;

		try {
			HttpGet get = new HttpGet(url);
			updateRequestHeader(get);

			if (cTimeout>0 && rTimeout>0){
				RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(cTimeout).setSocketTimeout(rTimeout).build();
				get.setConfig(requestConfig);
			}

			CloseableHttpResponse httpResponse = httpClient.execute(get);
			if (httpResponse==null){
				get.abort();
				return null;
			}

			HttpEntity entity = httpResponse.getEntity();
			if (entity!=null){
				ret = EntityUtils.toString(entity,"utf8");
			}
			closeHttpResponse(httpResponse);
		}catch (IOException e) {
			logger.error(e,e);
		}
		return ret;
	}
	
	/*how to create NameValuePair: example
	 * List<NameValuePair> list = new ArrayList<NameValuePair>();
	 * list.add(new BasicNameValuePair("username", "admin"));
	 * @param encode can be "UTF-8" and so on
	*/
	public String httpPost(String url,List<NameValuePair> params,String encode){
		String ret = "";
	
		try {
			HttpPost post = new HttpPost(url);
			updateRequestHeader(post);

			if (cTimeout>0 && rTimeout>0){
				RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(cTimeout).setSocketTimeout(rTimeout).build();
				post.setConfig(requestConfig);
			}

			UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(params,encode);
            post.setEntity(uefEntity);
            CloseableHttpResponse httpResponse = httpClient.execute(post);
            if (httpResponse==null){
            	post.abort();
                return null;
            }

            HttpEntity entity = httpResponse.getEntity();
            if (entity!=null){
                ret = EntityUtils.toString(entity,"UTF-8");
            }
            closeHttpResponse(httpResponse);
        } catch (Exception e){
            logger.error(e,e);
        }
		return ret;
	}
	
	/*how to create NameValuePair: example
	 * List<NameValuePair> list = new ArrayList<NameValuePair>();
	 * list.add(new BasicNameValuePair("username", "admin"));
	 * @param encode can be "UTF-8" and so on
	*/
	public String httpPost(String url,String postData,String encode){
		String ret = "";
				
		try {
			HttpPost post = new HttpPost(url);
			updateRequestHeader(post);

			if (cTimeout>0 && rTimeout>0){
				RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(cTimeout).setSocketTimeout(rTimeout).build();
				post.setConfig(requestConfig);
			}

			StringEntity strEntity = new StringEntity(postData);
            post.setEntity(strEntity);
            CloseableHttpResponse httpResponse = httpClient.execute(post);
            if (httpResponse==null){
            	post.abort();
                return null;
            }

            HttpEntity entity = httpResponse.getEntity();
            if (entity!=null){
                ret = EntityUtils.toString(entity,encode);
            }
            closeHttpResponse(httpResponse);
        } catch (Exception e){
            logger.error(e,e);
        }
		return ret;
	}

	//to get remote ip address
	public static String getRequestRemoteIp(HttpServletRequest request){
		String ip = request.getHeader("x-forwarded-for");
		if ((ip==null) || (ip.length()==0))
			ip = request.getRemoteAddr();
		return ip;
	}
	
	//to close httpClient object
	private void closeHttpClient(){
		if (httpClient==null)
			return;

		try {
			httpClient.close();
			httpClient = null;
		} catch (IOException e) {
			logger.error(e,e);
		}
	}
	
	//to close httpResponse
	private void closeHttpResponse(CloseableHttpResponse httpResponse){
		if (httpResponse==null)
			return;

		try {
			httpResponse.close();
		} catch (IOException e) {
			logger.error(e,e);
		}
	}

	/**
     * 创建SSL安全连接 
     * 
     * @return
     */
	private SSLConnectionSocketFactory createSSLConnSocketFactory(){
		try{
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy(){
				public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
					return true;
				}
			}).build();

			SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

			return csf;
        } catch (Exception e) {
            logger.error(e,e);
            return null;
        }
    }
	
	public static void main(String[] args){
	}
}
